package com.example.utils.util;

import org.springframework.format.annotation.DateTimeFormat;

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Date;

/**
 * TODO   jdk8获取时间的一些方法，新 API 基于 ISO 标准日历系统，java.time 包下的所有类都是不可变类型而且线程安全
 *          Instant：瞬时实例。
 *          LocalDate：本地日期，不包含具体时间 例如：2014-01-14 可以用来记录生日、纪念日、加盟日等。
 *          LocalTime：本地时间，不包含日期。
 *          LocalDateTime：组合了日期和时间，但不包含时差和时区信息。
 *          ZonedDateTime：最完整的日期时间，包含时区和相对 UTC 或格林威治的时差。
 *          新 API 还引入了 ZoneOffSet 和 ZoneId 类，使得解决时区问题更为简便。解析、格式化时间的 DateTimeFormatter 类也全部重新设计。
 *
 * @ClassName DateUtil
 * @Author xiaojiang
 * @Date 2022/2/14 下午 12:01
 */
public class DateUtil {

    /*
     *  TODO  获取今天的日期
     * @Author 江
     * @Date 2022/2/14 下午 12:03
     */
    public static void getCurrentDate() {
        LocalDate today = LocalDate.now();
        System.out.println("Today's Local date : " + today);  //Today's Local date : 2022-02-14

        //这个是作为对比
        Date date = new Date();
        System.out.println(date); // Mon Feb 14 12:06:52 CST 2022
    }

    /*
     *  TODO  获取年、月、日信息
     * @Author 江
     * @Date 2022/2/14 下午 12:04
     */
    public static void getDetailDate() {
        LocalDate today = LocalDate.now();
        int year = today.getYear();
        int month = today.getMonthValue();
        int day = today.getDayOfMonth();

        System.out.printf("Year : %d  Month : %d  day : %d t %n", year, month, day); //Year : 2022  Month : 2  day : 14 t
    }

    /**
     * TODO  处理特定日期
     *
     * @Author 江
     * @Date 2022/2/14 下午 12:05
     */
    public static void handleSpecilDate() {
        LocalDate dateOfBirth = LocalDate.of(2019, 01, 21);
        System.out.println("The specil date is : " + dateOfBirth); //The specil date is : 2019-01-21
    }

    /*
     *  TODO  判断两个日期是否相等
     * @Author 江
     * @Date 2022/2/14 下午 12:08
     */
    public static void compareDate() {
        LocalDate today = LocalDate.now();
        LocalDate date1 = LocalDate.of(2022, 02, 14);

        System.out.println("today:" + today); //today:2022-02-14
        System.out.println("date1:" + date1);  //date1:2022-02-14
        if (date1.equals(today)) {
            System.out.printf("TODAY %s and DATE1 %s are same date %n", today, date1);//TODAY 2022-02-14 and DATE1 2022-02-14 are same date
        }
    }

    /**
     * TODO  MonthDay类处理周期性的日期
     *
     * @Author 江
     * @Date 2022/2/14 下午 12:12
     */
    public static void cycleDate() {
        LocalDate today = LocalDate.now();
        LocalDate dateOfBirth = LocalDate.of(2022, 02, 14);

        MonthDay birthday = MonthDay.of(dateOfBirth.getMonth(), dateOfBirth.getDayOfMonth());
        MonthDay currentMonthDay = MonthDay.from(today);

        if (currentMonthDay.equals(birthday)) {
            System.out.println("Many Many happy returns of the day !!");
        } else {
            System.out.println("Sorry, today is not your birthday");//Many Many happy returns of the day !!
        }
    }

    /**
     * TODO  获取当前时间  默认的格式是hh:mm:ss:nnn
     *
     * @Author 江
     * @Date 2022/2/14 下午 12:16
     */
    public static void getCurrentTime() {
        LocalTime time = LocalTime.now();
        System.out.println("local time now : " + time);
    }

    /**
     * TODO  增加小时  plusHours() 方法替换 add()
     *
     * @Author 江
     * @Date 2022/2/14 下午 12:17
     */
    public static void plusHours() {
        LocalTime time = LocalTime.now();
        LocalTime newTime = time.plusHours(10); // 增加10小时
        System.out.println("now time : " + time);
        System.out.println("Time after 2 hours : " + newTime);
    }

    /**
     * TODO  如何计算一周后的日期
     *          LocalDate 日期不包含时间信息，它的 plus()方法用来增加天、周、月，ChronoUnit 类声明了这些时间单位。
     *          由于 LocalDate 也是不变类型，返回后一定要用变量赋值。
     *          可以用同样的方法增加 1 个月、1 年、1 小时、1 分钟甚至一个世纪，
     *          更多选项可以查看 Java 8 API 中的 ChronoUnit 类。
     *
     * @Author 江
     * @Date 2022/2/14 下午 12:17
     */
    public static void nextWeek() {
        LocalDate today = LocalDate.now();
        LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);    //使用变量赋值
        System.out.println("Today is : " + today);
        System.out.println("Date after 1 week : " + nextWeek);
    }

    /**
     * TODO   计算一年前或一年后的日期
     *          LocalDate 的 plus() 方法增加天数、周数或月数，
     *          这个例子我们利用 minus() 方法计算一年前的日期
     *
     * @Author 江
     * @Date 2022/2/14 下午 12:19
     */
    public static void minusDate() {
        LocalDate today = LocalDate.now();

        System.out.println("Now ：" + today);

        LocalDate previousYear = today.minus(1, ChronoUnit.YEARS);
        System.out.println("Date before 1 year : " + previousYear);

        LocalDate nextYear = today.plus(1, ChronoUnit.YEARS);
        System.out.println("Date after 1 year : " + nextYear);
    }

    /**
     * TODO  Clock 时钟类
     *          Clock 时钟类用于获取当时的时间戳，或当前时区下的日期时间信息。
     *          以前用到 System.currentTimeInMillis() 和 TimeZone.getDefault() 的地方都可用 Clock 替换
     *
     * @Author 江
     * @Date 2022/2/14 下午 12:22
     */
    public static void clock() {
        // 根据系统时间返回当前时间并设置为UTC。
        Clock clock = Clock.systemUTC();
        System.out.println("Clock : " + clock);

        // 根据系统时钟区域返回时间
        Clock defaultClock = Clock.systemDefaultZone();
        System.out.println("defaultClock : " + defaultClock);

        // 获取时间戳
        // 1. LocalDateTime包
        Timestamp timestamp = Timestamp.valueOf(LocalDateTime.now());
        System.out.println(timestamp.getTime());
        // 2. Instant包
        Instant instant = Instant.now();
        long timeStampMillis = instant.toEpochMilli();
        // 3. System.currentTimeInMillis()  较快
        System.out.println(timeStampMillis);
        System.out.println(System.currentTimeMillis());
    }

    /*
     *  TODO  如何用Java判断日期是早于还是晚于另一个日期
     *        isBefore() 和 isAfter() 用于比较日期。调用 isBefore() 方法时，如果给定日期小于当前日期则返回 true
     * @Author 江
     * @Date 2022/2/14 下午 12:31
     */
    public static void isBeforeOrIsAfter() {
        LocalDate today = LocalDate.now();

        System.out.println("today ：" + today); //today ：2022-02-14

        LocalDate tomorrow = LocalDate.of(2022, 02, 28);
        if (tomorrow.isAfter(today)) {
            System.out.println("Tomorrow comes after today"); // Tomorrow comes after today
        }

        //减去一天
        LocalDate yesterday = today.minus(1, ChronoUnit.DAYS);

        if (yesterday.isBefore(today)) {
            System.out.println("Yesterday is day before today"); // Yesterday is day before today
        }
    }

    /**
     * TODO  使用 YearMonth类处理特定的日期
     *
     * @Author 江
     * @Date 2022/2/14 下午 12:35
     */
    public static void checkCardExpiry() {
        YearMonth currentYearMonth = YearMonth.now();
        // Days in month year 2022-02: 28
        System.out.printf("Days in month year %s: %d%n", currentYearMonth, currentYearMonth.lengthOfMonth());

        YearMonth creditCardExpiry = YearMonth.of(2028, Month.FEBRUARY);
        // Your credit card expires on 2028-02
        System.out.printf("Your credit card expires on %s %n", creditCardExpiry);
    }

    /**
     * TODO    检查闰年
     *          isLeapYear() 判断该实例是否是一个闰年
     *
     * @Author 江
     * @Date 2022/2/14 下午 12:43
     */
    public static void isLeapYear() {
        LocalDate today = LocalDate.now();
        // Today year ：2022
        System.out.println("Today year ：" + today.getYear());
        if (today.isLeapYear()) {
            System.out.println("This year is Leap year");
        } else {
            System.out.println("2022 is not a Leap year");
        }
        //  2022 is not a Leap year
    }

    /**
     * TODO    计算两个日期之间的天数和月数
     *
     * @return void
     * @Param :
     * @Author 江
     * @Date 2022/2/14 下午 12:44
     */
    public static void calcDateDays() {
        LocalDate today = LocalDate.now();
        System.out.println("Today ：" + today);

        LocalDate java8Release = LocalDate.of(2019, 12, 30);
        System.out.println("java8Release ：" + java8Release);

        Period periodToNextJavaRelease = Period.between(java8Release, today);
        //Today ：2022-02-14
        //java8Release ：2019-12-30
        //两个日期相距：2 年，1 月，15 天
        System.out.println(String.format(
                "两个日期相距：%s 年，%s 月，%s 天  ",
                periodToNextJavaRelease.getYears(),
                periodToNextJavaRelease.getMonths(),
                periodToNextJavaRelease.getDays()));
        // Period中时间区间如果是2年1个月15天。那么通过getDays()方法获得就是第三个存储15天。而不是2年1个月15天总共有多少天。
        //那么如果想算两个日期直接的距离该怎么办呢？可采用下面的方法。得出的就是总天数
        long days = today.toEpochDay() - java8Release.toEpochDay();
        System.out.println("两个日期相距 : " + days);
    }

    /**
     * TODO  使用预定义的格式化工具去解析或格式化日期
     *
     * @return void
     * @Param :
     * @Author 江
     * @Date 2022/2/14 下午 12:53
     */
    public static void formateDate() {
        String dayAfterTommorrow = "20180210";
        LocalDate formatted = LocalDate.parse(dayAfterTommorrow, DateTimeFormatter.BASIC_ISO_DATE);
        // 2018-02-10
        System.out.printf("Date generated from String %s is %s %n", dayAfterTommorrow, formatted);
        // 其他格式
        // 2057-08-11
        DateTimeFormatter isoLocalDate = DateTimeFormatter.ISO_LOCAL_DATE;
        DateTimeFormatter isoDate = DateTimeFormatter.ISO_DATE;

        // 14:30:15.312
        DateTimeFormatter isoTime = DateTimeFormatter.ISO_TIME;
        DateTimeFormatter isoLocalTime = DateTimeFormatter.ISO_LOCAL_TIME;
        // 2050-08-11T14:30:15.312
        DateTimeFormatter isoDateTime = DateTimeFormatter.ISO_DATE_TIME;
        DateTimeFormatter isoLocaDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
    }

    /**
     * @TODO JDK 1.8 之后格式化代码的方法 (线程安全，格式化 JDK 1.8 提供的时间类型)
     * @Param :
     * @Return void
     * @Author 江
     * @Date 2022/10/18 22:12
     */
    public static void dateTimeFormat() {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String format = dateTimeFormatter.format(LocalTime.now());
        System.out.println(format);
    }

    /**
     * @TODO JDK 1.8 之前的重要格式化时间方法 (非线程安全，格式化 Date 类型)
     * @Param :
     * @Return void
     * @Author 江
     * @Date 2022/10/18 22:08
     */
    public static void simpleDateFormat() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String format = simpleDateFormat.format(new Date());
        System.out.println(format);
        //  long类型转String
        long l = System.currentTimeMillis();
        Date date = new Date(l);
        String format1 = simpleDateFormat.format(date);
        System.out.println(format1);
        // String 转Date
        String datetime = "2022-01-01 12:12:12";
        try {
            Date parse = simpleDateFormat.parse(datetime);
            System.out.println(parse);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

    /**
     * @TODO Spring boot全局格式化时间
     * 在配置文件 application.properties(或 applocation.yml) 添加以下配置
     * # 格式化全局时间字段
     * spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
     * # 指定时间区域类型
     * spring.jackson.time-zone=GMT+8
     * 原理：
     * Controller 返回数据时，会自动调用 Spring boot 框架中内置的 JSON 框架 jackson，
     * 对返回的数据进行统一的 JSON 格式化处理，在处理的过程中会判断配置文件中是否设置了
     * "spring.jackson.date-format=yyyy-MM-dd HH:mm:ss",
     * 如果设置了，那么 jackson 框架在对时间类型的字段输出时就会执行时间格式化的处理
     * 指定时间区域类型 "spring.jackson.time-zone=GMT+8" 的原因:
     * 如果不指定时间区域类型，格式化后的时间就会少8小时，
     * 这是因为(中国)所处的时间区域比世界时间(GMT)少8个小时导致的
     * 部分时间格式化:
     * 某些场景下不需要全局时间统一处理
     * 在实体类中的字段上添加 @JsonFormat 注解
     * @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "CMT+8")
     * private Date creattime;
     * 原理和全局格式化类似，都是在返回数据前对相应的字段进行时间格式化处理
     * 输出结果时隐藏字段的注解 @JsonIgnore
     * @JsonIgnore private Date creattime;
     */

    public static void forDateTimeFormatter() {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime localDateTime = now.plusHours(10);
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
        System.out.println("加时之后" + localDateTime.format(dateTimeFormatter));
    }
}
